/*
 * Copyright 2009-2010 the original author or authors.
 *
 * Licensed under the Apache license, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.internna.iwebjtracker.services;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.internna.iwebjtracker.issue.model.Issue;
import org.internna.iwebjtracker.model.TrackerUser;
import org.internna.iwebmvc.dao.DAO;
import org.internna.iwebmvc.model.User;
import org.internna.iwebmvc.security.UserManager;
import org.internna.iwebmvc.utils.Assert;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.transaction.annotation.Transactional;

/**
 * Generates activity reports using a request parameter.
 *
 * @author Jose Noheda
 * @since 1.0
 */
public final class RequestActivityReporter implements ActivityReporter {

    private DAO dao;
    private UserManager userManager;
    private TrackerUserManager trackerUserManager;

    /**
     * Spring required injection point.
     *
     * @param dao a non null instance
     */
    @Required public void setDao(DAO dao) {
        Assert.notNull(dao);
        this.dao = dao;
    }

    /**
     * Spring required injection point.
     *
     * @param userManager a non null instance
     */
    @Required public void setUserManager(UserManager userManager) {
        Assert.notNull(userManager);
        this.userManager = userManager;
    }

    /**
     * Spring required injection point.
     *
     * @param trackerUserManager a non null instance
     */
    @Required public void setTrackerUserManager(TrackerUserManager trackerUserManager) {
        Assert.notNull(trackerUserManager);
        this.trackerUserManager = trackerUserManager;
    }

    /**
     * Obtain the activity data filtered by permissions of the current user grouped by activity type.
     *
     * @return
     */
    @Transactional(readOnly = true)
    @Override public List<Object[]> getRecentActivityByType() {
        Map.Entry<String, Map<String, Object>> security = getSecurityClause().entrySet().iterator().next();
        return (List) dao.executeQuery("SELECT a.type, COUNT(a.type) FROM Activity a WHERE " + security.getKey() + " GROUP BY a.type", 0, 100, security.getValue());
    }

    /**
     * Obtain the activity data filtered by permissions of the current user grouped by activity priority.
     *
     * @return a group of type of activity and a number.
     */
    @Transactional(readOnly = true)
    @Override public List<Object[]> getRecentActivityByPriority() {
        Map.Entry<String, Map<String, Object>> security = getSecurityClause().entrySet().iterator().next();
        return (List) dao.executeQuery("SELECT a.type, COUNT(a.type) FROM Activity a WHERE " + security.getKey() + " GROUP BY a.type", 0, 100, security.getValue());
    }

    private Map<String, Map<String, Object>> getSecurityClause() {
        Map<String, Object> securityClause = null;
        Map<String, Map<String, Object>> clauses = new HashMap<String, Map<String, Object>>(1);
        String clause = "( ( a.publicView = true )";
        User user = userManager.getActiveUser();
        if ((user != null) && (!user.isAnonymous())) {
            clause += " OR ( :owner IN ELEMENTS(a.viewers) )";
            securityClause = new HashMap<String, Object>(1);
            securityClause.put("owner", user);
        }
        clause += " )";
        clauses.put(clause, securityClause);
        return clauses;
    }

    /**
     * Generates user statistics using SQL queries.
     *
     * @return a map with two keys (TOTAL_USERS, TOTAL_DEVELOPERS, TOTAL_DISABLED_USERS)
     */
    @Override public Map<String, Object> getUsersStatistics() {
        Map<String, Object> statistics = new HashMap<String, Object>();
        statistics.put(TOTAL_USERS, dao.executeQuery("SELECT COUNT(username) FROM UserImpl").get(0));
        statistics.put(TOTAL_DISABLED_USERS, dao.executeQuery("SELECT COUNT(username) FROM UserImpl WHERE enabled = false").get(0));
        return statistics;
    }

    /**
     * Generates project statistics using SQL queries.
     *
     * @return a map with three keys (TOTAL_ACTIVITY, TOTAL_PROJECTS, TOTAL_DEVELOPERS)
     */
    @Override public Map<String, Object> getProjectsStatistics() {
        Map<String, Object> statistics = new HashMap<String, Object>();
        statistics.put(TOTAL_ACTIVITY, dao.executeQuery("SELECT COUNT(id) FROM Activity").get(0));
        statistics.put(TOTAL_PROJECTS, dao.executeQuery("SELECT COUNT(id) FROM DefaultProject").get(0));
        Map<String, Object> status = new HashMap<String, Object>(1);
        status.put("status", Issue.Status.CLOSED);
        statistics.put(TOTAL_OPEN_ISSUES, dao.executeQuery("SELECT COUNT(id) FROM DefaultIssue WHERE status != :status", 0, 99999, status).get(0));
        status.put("blocker", Issue.Severity.BLOCKER);
        status.put("critical", Issue.Severity.CRITICAL);
        statistics.put(TOTAL_CRITICAL_OPEN_ISSUES, dao.executeQuery("SELECT COUNT(id) FROM DefaultIssue WHERE status != :status AND (severity = :critical OR severity = :blocker)", 0, 99999, status).get(0));
        status.clear();
        status.put("assigned", Issue.Status.ASSIGNED);
        status.put("wip", Issue.Status.WORK_IN_PROGRESS);
        statistics.put(TOTAL_ASSIGNED, dao.executeQuery("SELECT COUNT(id) FROM DefaultIssue WHERE status = :assigned OR status = :wip)", 0, 99999, status).get(0));
        statistics.put(TOTAL_DEVELOPERS, dao.executeNativeQuery("SELECT COUNT(DISTINCT developer) FROM PROJECTS_DEVELOPERS").get(0));
        return statistics;
    }

    /**
     * Generates project statistics for the user in session using SQL queries.
     *
     * @return a map with three keys (TOTAL_ACTIVITY, TOTAL_PROJECTS, TOTAL_DEVELOPERS)
     */
    @Override public Map<String, Object> getProjectsStatisticsForCurrentUser() {
        Map<String, Object> statistics = new HashMap<String, Object>();
        User user = userManager.getActiveUser();
        if ((user != null) && (!user.isAnonymous())) {
            TrackerUser trackerUser = trackerUserManager.getTrackerUser(user.getUsername());
            Map<String, Object> params = new HashMap<String, Object>(1);
            params.put("user", trackerUser);
            statistics.put(TOTAL_PROJECTS, dao.executeNativeQuery("SELECT COUNT(observer) FROM PROJECTS_OBSERVERS WHERE observer = :user", 0, 100, params).get(0));
            statistics.put(TOTAL_DEVELOPERS, dao.executeNativeQuery("SELECT COUNT(developer) FROM PROJECTS_DEVELOPERS WHERE developer = :user", 0, 100, params).get(0));
        }
        return statistics;
    }

}
